﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Interactivity;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;

namespace LogoFX.Infra.Interactivity.Behaviors
{
    /// <summary>
    /// Adds watermark behavior to any <see cref="TextBox"/> control.
    /// </summary>
    public class WatermarkBehavior : Behavior<TextBox>
    {
        #region Fields
        private Canvas _watermarkHolder = null;
        private TextBlock _watermark = null;
        #endregion

        #region Dependency Properties

        #region WatermarkText
        /// <summary>
        /// Gets or sets the watermark text.
        /// </summary>
        public static readonly DependencyProperty WatermarkTextProperty =
            DependencyProperty.Register(
                "WatermarkText",
                typeof(string),
                typeof(WatermarkBehavior),
                new PropertyMetadata(null));

        /// <summary>
        /// Gets or sets the watermark text.
        /// </summary>
        /// <value>
        /// The watermark text.
        /// </value>
        public string WatermarkText
        {
            get { return (string)GetValue(WatermarkTextProperty); }
            set { SetValue(WatermarkTextProperty, value); }
        }
        #endregion

        #region WatermarkFontSize

        /// <summary>
        /// Gets or sets the size of the watermark font.
        /// </summary>
        public static readonly DependencyProperty WatermarkFontSizeProperty =
             DependencyProperty.Register(
                "WatermarkFontSize",
                typeof(double),
                typeof(WatermarkBehavior),
                new PropertyMetadata(12.0));

        /// <summary>
        /// Gets or sets the size of the watermark font.
        /// </summary>
        /// <value>
        /// The size of the watermark font.
        /// </value>
        public double WatermarkFontSize
        {
            get { return (double)GetValue(WatermarkFontSizeProperty); }
            set { SetValue(WatermarkFontSizeProperty, value); }
        }

        #endregion

        #region WatermarkFontFamily
        /// <summary>
        ///  Gets or sets the watermark font family.
        /// </summary>
        public static readonly DependencyProperty WatermarkFontFamilyProperty =
             DependencyProperty.Register(
                "WatermarkFontFamily",
                typeof(FontFamily),
                typeof(WatermarkBehavior),
                new PropertyMetadata(new FontFamily("Century Gothic")));

        /// <summary>
        /// Gets or sets the watermark font family.
        /// </summary>
        /// <value>
        /// The watermark font family.
        /// </value>
        public FontFamily WatermarkFontFamily
        {
            get { return (FontFamily)GetValue(WatermarkFontFamilyProperty); }
            set { SetValue(WatermarkFontFamilyProperty, value); }
        }
        #endregion

        #region WatermarkFontWeight
        /// <summary>
        /// Gets or sets the watermark font weight.
        /// </summary>
        public static readonly DependencyProperty WatermarkFontWeightProperty =
             DependencyProperty.Register(
                "WatermarkFontWeight",
                typeof(FontWeight),
                typeof(WatermarkBehavior),
                new PropertyMetadata(FontWeights.Thin));

        /// <summary>
        /// Gets or sets the watermark font weight.
        /// </summary>
        /// <value>
        /// The watermark font weight.
        /// </value>
        public FontWeight WatermarkFontWeight
        {
            get { return (FontWeight)GetValue(WatermarkFontWeightProperty); }
            set { SetValue(WatermarkFontWeightProperty, value); }
        }
        #endregion

        #region WatermarkFontStyle
        /// <summary>
        /// Gets or sets the watermark font style.
        /// </summary>
        public static readonly DependencyProperty WatermarkFontStyleProperty =
             DependencyProperty.Register(
                "WatermarkFontStyle",
                typeof(FontStyle),
                typeof(WatermarkBehavior),
                new PropertyMetadata(FontStyles.Italic));

        /// <summary>
        /// Gets or sets the watermark font style.
        /// </summary>
        /// <value>
        /// The watermark font style.
        /// </value>
        public FontStyle WatermarkFontStyle
        {
            get { return (FontStyle)GetValue(WatermarkFontStyleProperty); }
            set { SetValue(WatermarkFontStyleProperty, value); }
        }
        #endregion WatermarkFontStyle

        #region WatermarkHorizontalAlignment

        /// <summary>
        /// Gets or sets the watermark horizontal alignment.
        /// </summary>
        public static readonly DependencyProperty WatermarkHorizontalAlignmentProperty =
             DependencyProperty.Register(
                "WatermarHorizontalAlignment",
                typeof(HorizontalAlignment),
                typeof(WatermarkBehavior),
                new PropertyMetadata(HorizontalAlignment.Stretch));

        /// <summary>
        /// Gets or sets the watermark horizontal alignment.
        /// </summary>
        /// <value>
        /// The watermark horizontal alignment.
        /// </value>
        public HorizontalAlignment WatermarkHorizontalAlignment
        {
            get { return (HorizontalAlignment)GetValue(WatermarkHorizontalAlignmentProperty); }
            set { SetValue(WatermarkHorizontalAlignmentProperty, value); }
        }
        #endregion WatermarkHorizontalAlignmentProperty

        #region WatermarkFontStretch
        /// <summary>
        /// Gets or sets the watermark font stretch.
        /// </summary>
        public static readonly DependencyProperty WatermarkFontStretchProperty =
             DependencyProperty.Register(
                "WatermarkFontStretch",
                typeof(FontStretch),
                typeof(WatermarkBehavior),
                new PropertyMetadata(FontStretches.Normal));

        /// <summary>
        /// Gets or sets the watermark font stretch.
        /// </summary>
        /// <value>
        /// The watermark font stretch.
        /// </value>
        public FontStretch WatermarkFontStretch
        {
            get { return (FontStretch)GetValue(WatermarkFontStretchProperty); }
            set { SetValue(WatermarkFontStretchProperty, value); }
        }
        #endregion WatermarkFontStretch

        #region WatermarkForeground
        /// <summary>
        /// Gets or sets the watermark foreground.
        /// </summary>
        public static readonly DependencyProperty WatermarkForegroundProperty =
             DependencyProperty.Register(
                "WatermarkForeground",
                typeof(Brush),
                typeof(WatermarkBehavior),
                new PropertyMetadata(new SolidColorBrush(Colors.Gray)));

        /// <summary>
        /// Gets or sets the watermark foreground.
        /// </summary>
        /// <value>
        /// The watermark foreground.
        /// </value>
        public Brush WatermarkForeground
        {
            get { return (Brush)GetValue(WatermarkForegroundProperty); }
            set { SetValue(WatermarkForegroundProperty, value); }
        }
        #endregion

        #region WatermarkMargin
        /// <summary>
        /// Gets or sets the watermark margin.
        /// </summary>
        public static readonly DependencyProperty WatermarkMarginProperty =
             DependencyProperty.Register(
                "WatermarkMargin",
                typeof(Thickness),
                typeof(WatermarkBehavior),
                new PropertyMetadata(default(Thickness)));

        /// <summary>
        /// Gets or sets the watermark margin.
        /// </summary>
        /// <value>
        /// The watermark margin.
        /// </value>
        public Thickness WatermarkMargin
        {
            get { return (Thickness)GetValue(WatermarkMarginProperty); }
            set { SetValue(WatermarkMarginProperty, value); }
        }
        #endregion

        #endregion

        #region Methods

        /// <summary>
        /// Called when behavior attached.
        /// </summary>
        protected override void OnAttached()
        {
            base.OnAttached();

            TextBox textBox = this.AssociatedObject;

            _watermarkHolder = new Canvas();

            _watermark = new TextBlock();

            Binding textBinding = new Binding("WatermarkText");
            textBinding.Source = this;
            BindingOperations.SetBinding(_watermark, TextBlock.TextProperty, textBinding);

            Binding horizontalAlignmentBinding = new Binding("WatermarkHorizontalAlignment") { Source = this };
            BindingOperations.SetBinding(_watermark, FrameworkElement.HorizontalAlignmentProperty, horizontalAlignmentBinding);

            Binding watermarkFontSizeBinding = new Binding("WatermarkFontSize") { Source = this };
            BindingOperations.SetBinding(_watermark, TextBlock.FontSizeProperty, watermarkFontSizeBinding);

            Binding watermarkFontFamily = new Binding("WatermarkFontFamily");
            watermarkFontFamily.Source = this;
            BindingOperations.SetBinding(_watermark, TextBlock.FontFamilyProperty, watermarkFontFamily);

            Binding watermarkFontWeight = new Binding("WatermarkFontWeight");
            watermarkFontWeight.Source = this;
            BindingOperations.SetBinding(_watermark, TextBlock.FontWeightProperty, watermarkFontWeight);

            Binding watermarkFontStyle = new Binding("WatermarkFontStyle");
            watermarkFontStyle.Source = this;
            BindingOperations.SetBinding(_watermark, TextBlock.FontStyleProperty, watermarkFontStyle);

            Binding watermarkFontStretch = new Binding("WatermarkFontStretch");
            watermarkFontStretch.Source = this;
            BindingOperations.SetBinding(_watermark, TextBlock.FontStretchProperty, watermarkFontStretch);

            Binding watermarkForeground = new Binding("WatermarkForeground");
            watermarkForeground.Source = this;
            BindingOperations.SetBinding(_watermark, TextBlock.ForegroundProperty, watermarkForeground);

            Binding watermarkMargin = new Binding("WatermarkMargin");
            watermarkMargin.Source = this;
            BindingOperations.SetBinding(_watermark, FrameworkElement.MarginProperty, watermarkMargin);


            _watermarkHolder.Children.Add(_watermark);

            textBox.SizeChanged += OnSizeChanged;
            
            //05.03.12: evgeni fixing bug where text received from binding
            //textBox.TextChanged += OnTextBoxTextChanged;

            textBox.NotifyOn("Text", OnTextChanged);

            BindingExpression expresion = textBox.GetBindingExpression(Control.BackgroundProperty);

            if (expresion != null) //we have something bound to background of textbox - move it to background of grid
            {
                textBox.NotifyOn("Background", OnOriginalBackgroundSet);
            }
            else
            {
                if (textBox.Background != null)
                    _watermarkHolder.Background = textBox.Background;

                UpdateWatermark();
            }
        }

        /// <summary>
        /// Called when behavior detaching.
        /// </summary>
        protected override void OnDetaching()
        {
            base.OnDetaching();

            TextBox textBox = this.AssociatedObject;
            //textBox.TextChanged -= OnTextBoxTextChanged;
            textBox.SizeChanged -= OnSizeChanged;
        }

        /// <summary>
        /// Called when adorned textbox size changed.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.SizeChangedEventArgs"/> instance containing the event data.</param>
        private void OnSizeChanged(object sender, SizeChangedEventArgs e)
        {
            _watermarkHolder.Width = e.NewSize.Width;
            _watermarkHolder.Height = e.NewSize.Height;

            UpdateWatermark();
        }

        /// <summary>
        /// Updates the watermark.
        /// </summary>
        private void UpdateWatermark()
        {
            TextBox textBox = this.AssociatedObject;
            WriteableBitmap bitmap = new WriteableBitmap(_watermarkHolder, null);
            textBox.Background = new ImageBrush() { ImageSource = bitmap, Stretch = Stretch.None };
        }

        /// <summary>
        /// Called when background of adorner change.
        /// </summary>
        /// <param name="newValue">The new value.</param>
        /// <param name="oldValue">The old value.</param>
        private void OnBackgroundChange(object newValue, object oldValue)
        {
            UpdateWatermark();
        }

        /// <summary>
        /// Called when original background set. Used as workaround to solve not initialized binding. 
        /// </summary>
        /// <param name="newValue">The new value.</param>
        /// <param name="oldValue">The old value.</param>
        private void OnOriginalBackgroundSet(object newValue, object oldValue)
        {
            AssociatedObject.UnNotifyOn("Background");

            _watermarkHolder.NotifyOn("Background", OnBackgroundChange);

            BindingExpression expresion = AssociatedObject.GetBindingExpression(Control.BackgroundProperty);
            Binding originalBinding = expresion.ParentBinding;

            Binding clonedBinding = new Binding();
            clonedBinding.Path = originalBinding.Path;
            clonedBinding.Source = expresion.DataItem;

            _watermarkHolder.SetBinding(Panel.BackgroundProperty, clonedBinding);

            AssociatedObject.ClearValue(Control.BackgroundProperty); //clear old binding

        }

        private void OnTextChanged(object newValue, object oldValue)
        {
            bool watermarkVisible = string.IsNullOrEmpty((string)newValue);

            if (watermarkVisible && _watermark.Visibility == Visibility.Collapsed)
            {
                _watermark.Visibility = Visibility.Visible;
                UpdateWatermark();
            }

            if (!watermarkVisible && _watermark.Visibility == Visibility.Visible)
            {
                _watermark.Visibility = Visibility.Collapsed;
                UpdateWatermark();
            }
        }

        ///// <summary>
        ///// Called when text box text changed. This method handles the logic of watermark.
        ///// </summary>
        ///// <param name="sender">The sender.</param>
        ///// <param name="e">The <see cref="System.Windows.Controls.TextChangedEventArgs"/> instance containing the event data.</param>
        //private void OnTextBoxTextChanged(object sender, TextChangedEventArgs e)
        //{
        //    bool watermarkVisible = AssociatedObject.Text.Length == 0;

        //    if (watermarkVisible && _watermark.Visibility == Visibility.Collapsed)
        //    {
        //        _watermark.Visibility = Visibility.Visible;
        //        UpdateWatermark();
        //    }

        //    if (!watermarkVisible && _watermark.Visibility == Visibility.Visible)
        //    {
        //        _watermark.Visibility = Visibility.Collapsed;
        //        UpdateWatermark();
        //    }
        //}

        #endregion
    }
}

